package edu.purdue.cs.ds.vss;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Iterator;

import edu.purdue.cs.ds.ServerSets;

public class SubSharingTest2 {
	public static void main(String[] args) {
		
		//Generate server sets 
		int n = 3;
		int k = 1;
		
		//Set up n ShareStore instances
		ArrayList<ShareStore> stores = new ArrayList<ShareStore>();
		for(int i = 0; i < n; i++) {
			stores.add(new ShareStore());
		}		
		
		ServerSets serverSets = new ServerSets(n, k);
		int l = serverSets.getL();
		ArrayList<ArrayList<Integer>> sets = serverSets.constructServerSets();
		
		System.out.println("l : " + l);
		SecureRandom sr = new SecureRandom();
		BigInteger r =  new BigInteger(Constants.q.bitLength() - 1, sr);
		BigInteger s =  new BigInteger(Constants.q.bitLength() - 1, sr);

		System.out.println("Main secret " + s);
		
		Generator g = new Generator(s, r , l);
		VerifiableSharing vs = g.generateVerifiableSharing();
		ArrayList<BigInteger> shares = vs.getS();
		
		ArrayList<Share> shareList = new ArrayList<Share>();
		ParentIndex parentIndex = new ParentIndex(0, 0, null);
		for(int i = 0; i < shares.size(); i ++) {
			shareList.add(new Share(shares.get(i), i, parentIndex));
		}
		
		//Depth 1
		IndexSets is = new IndexSets();
		ArrayList<ArrayList<Share>> serverShares = is.generateShareSets2(n, shareList, sets);
		
		
		//Store the server shares in each sharestore
		for(int i = 0; i < n; i ++) {
			ShareStore st = stores.get(i);
			ArrayList<Share> tmpSh = serverShares.get(i);
			for (Iterator<Share> iterator = tmpSh.iterator(); iterator.hasNext();) {
				Share tmp = iterator.next();
				st.update(tmp);
			}
		}
		Reconstructor reconstructor = new Reconstructor();
		System.out.println("First reconstruction : " + s.equals(reconstructor.reconstructSecretFromStores(stores, n, k)));
		
		//Print each store
//		for(int i = 0; i < n; i ++) {
//			ShareStore st = stores.get(i);
//			System.out.println(st);
//		}
//		
		//Now lets create sub sharings for each server's shares and add them to respective stores
		//Here we will create a new set of stores for each server
		
		ArrayList<ShareStore> storesAtDepth2 = new ArrayList<ShareStore>();
		for(int i = 0; i < n; i++) {
			storesAtDepth2.add(new ShareStore());
		}	
		
		for(int i = 0; i < n; i ++) { //For each server
			ShareStore st = stores.get(i);
			ArrayList<Share> tmpShares = st.getShares();
			for (Iterator<Share> iterator = tmpShares.iterator(); iterator.hasNext();) { //For each share
				Share share = iterator.next(); //This is the parent share now
				ParentIndex pi = new ParentIndex(i, share.getIndex(), share.getParentIndex());
				
				//Do sub sharing
				//We use the same random becuase we don't care about verification here
				Generator tmpG = new Generator(share.getS(), r , l);
				VerifiableSharing tmpVs = tmpG.generateVerifiableSharing();
				ArrayList<BigInteger> tmpSh = tmpVs.getS();
				ArrayList<Share> tmpShareList = new ArrayList<Share>();
				for(int j = 0; j < tmpSh.size(); j ++) {
					tmpShareList.add(new Share(tmpSh.get(j), j, pi));
				}
				
				//We use the same server sets here
				ArrayList<ArrayList<Share>> tmpServerShares = is.generateShareSets2(n, tmpShareList, sets);
				//Store the server shares in each sharestore
				for(int j = 0; j < n; j ++) {
					ShareStore tmpSt = storesAtDepth2.get(j);
					ArrayList<Share> tmpShList = tmpServerShares.get(j);
					for (Iterator<Share> it = tmpShList.iterator(); it.hasNext();) {
						Share tmp = it.next();
						//Create the Verifiable Share using tmp
						tmpSt.update(tmp);
					}
				}
			}
		}
		
		//Print each store
//		System.out.println("AFTER SUBSHARING!!! ");
//		for(int i = 0; i < n; i ++) {
//			ShareStore st = storesAtDepth2.get(i);
//			System.out.println(st);
//		}
		
		//select k+1 stores and give those to reconstructor
		ArrayList<ShareStore> reconStores = new ArrayList<ShareStore>();
		for(int i = 0; i < k+1; i++) {
			reconStores.add(storesAtDepth2.get(i));
		}

		
		BigInteger reconstructedSecret = reconstructor.reconstructSecretFromStores(reconStores, n, k);
		System.out.println(reconstructedSecret);
		System.out.println("Reconstruction successful : " + s.equals(reconstructedSecret));
		
		

		ArrayList<ShareStore> storesAtDepth3 = new ArrayList<ShareStore>();
		for(int i = 0; i < n; i++) {
			storesAtDepth3.add(new ShareStore());
		}	

		
		for(int i = 0; i < n; i ++) { //For each server
			ShareStore st = storesAtDepth2.get(i);
			ArrayList<Share> tmpShares = st.getShares();
			for (Iterator<Share> iterator = tmpShares.iterator(); iterator.hasNext();) { //For each share
				Share share = iterator.next(); //This is the parent share now
				ParentIndex pi = new ParentIndex(i, share.getIndex(), share.getParentIndex());
				
				//Do sub sharing
				//We use the same random becuase we don't care about verification here
				Generator tmpG = new Generator(share.getS(), r , l);
				VerifiableSharing tmpVs = tmpG.generateVerifiableSharing();
				ArrayList<BigInteger> tmpSh = tmpVs.getS();
				ArrayList<Share> tmpShareList = new ArrayList<Share>();
				for(int j = 0; j < tmpSh.size(); j ++) {
					tmpShareList.add(new Share(tmpSh.get(j), j, pi));
				}
				
				//We use the same server sets here
				ArrayList<ArrayList<Share>> tmpServerShares = is.generateShareSets2(n, tmpShareList, sets);
				//Store the server shares in each sharestore
				for(int j = 0; j < n; j ++) {
					ShareStore tmpSt = storesAtDepth3.get(j);
					ArrayList<Share> tmpShList = tmpServerShares.get(j);
					for (Iterator<Share> it = tmpShList.iterator(); it.hasNext();) {
						Share tmp = it.next();
						//Create the Verifiable Share using tmp
						tmpSt.update(tmp);
					}
				}
			}
		}
		
		//select k+1 stores and give those to reconstructor
		ArrayList<ShareStore> reconStores2 = new ArrayList<ShareStore>();
		for(int i = 0; i < k+1; i++) {
			reconStores2.add(storesAtDepth3.get(i));
		}

		BigInteger reconstructedSecret2 = reconstructor.reconstructSecretFromStores(reconStores2, n, k);
		System.out.println(reconstructedSecret2);
		System.out.println("Reconstruction successful : " + s.equals(reconstructedSecret2));
		
		
		

		ArrayList<ShareStore> storesAtDepth4 = new ArrayList<ShareStore>();
		for(int i = 0; i < n; i++) {
			storesAtDepth4.add(new ShareStore());
		}	

		
		for(int i = 0; i < n; i ++) { //For each server
			ShareStore st = storesAtDepth3.get(i);
			ArrayList<Share> tmpShares = st.getShares();
			for (Iterator<Share> iterator = tmpShares.iterator(); iterator.hasNext();) { //For each share
				Share share = iterator.next(); //This is the parent share now
				ParentIndex pi = new ParentIndex(i, share.getIndex(), share.getParentIndex());
				
				//Do sub sharing
				//We use the same random becuase we don't care about verification here
				Generator tmpG = new Generator(share.getS(), r , l);
				VerifiableSharing tmpVs = tmpG.generateVerifiableSharing();
				ArrayList<BigInteger> tmpSh = tmpVs.getS();
				ArrayList<Share> tmpShareList = new ArrayList<Share>();
				for(int j = 0; j < tmpSh.size(); j ++) {
					tmpShareList.add(new Share(tmpSh.get(j), j, pi));
				}
				
				//We use the same server sets here
				ArrayList<ArrayList<Share>> tmpServerShares = is.generateShareSets2(n, tmpShareList, sets);
				//Store the server shares in each sharestore
				for(int j = 0; j < n; j ++) {
					ShareStore tmpSt = storesAtDepth4.get(j);
					ArrayList<Share> tmpShList = tmpServerShares.get(j);
					for (Iterator<Share> it = tmpShList.iterator(); it.hasNext();) {
						Share tmp = it.next();
						//Create the Verifiable Share using tmp
						tmpSt.update(tmp);
					}
				}
			}
		}
		
		//select k+1 stores and give those to reconstructor
		ArrayList<ShareStore> reconStores3 = new ArrayList<ShareStore>();
		for(int i = 0; i < k+1; i++) {
			reconStores3.add(storesAtDepth4.get(i));
		}

		BigInteger reconstructedSecret3 = reconstructor.reconstructSecretFromStores(reconStores3, n, k);
		System.out.println(reconstructedSecret3);
		System.out.println("Reconstruction successful : " + s.equals(reconstructedSecret3));
		
		
		ArrayList<ShareStore> storesAtDepth5 = new ArrayList<ShareStore>();
		for(int i = 0; i < n; i++) {
			storesAtDepth5.add(new ShareStore());
		}	

		
		for(int i = 0; i < n; i ++) { //For each server
			ShareStore st = storesAtDepth4.get(i);
			ArrayList<Share> tmpShares = st.getShares();
			for (Iterator<Share> iterator = tmpShares.iterator(); iterator.hasNext();) { //For each share
				Share share = iterator.next(); //This is the parent share now
				ParentIndex pi = new ParentIndex(i, share.getIndex(), share.getParentIndex());
				
				//Do sub sharing
				//We use the same random becuase we don't care about verification here
				Generator tmpG = new Generator(share.getS(), r , l);
				VerifiableSharing tmpVs = tmpG.generateVerifiableSharing();
				ArrayList<BigInteger> tmpSh = tmpVs.getS();
				ArrayList<Share> tmpShareList = new ArrayList<Share>();
				for(int j = 0; j < tmpSh.size(); j ++) {
					tmpShareList.add(new Share(tmpSh.get(j), j, pi));
				}
				
				//We use the same server sets here
				ArrayList<ArrayList<Share>> tmpServerShares = is.generateShareSets2(n, tmpShareList, sets);
				//Store the server shares in each sharestore
				for(int j = 0; j < n; j ++) {
					ShareStore tmpSt = storesAtDepth5.get(j);
					ArrayList<Share> tmpShList = tmpServerShares.get(j);
					for (Iterator<Share> it = tmpShList.iterator(); it.hasNext();) {
						Share tmp = it.next();
						//Create the Verifiable Share using tmp
						tmpSt.update(tmp);
					}
				}
			}
		}
		
		//select k+1 stores and give those to reconstructor
		ArrayList<ShareStore> reconStores4 = new ArrayList<ShareStore>();
		for(int i = 0; i < k+1; i++) {
			reconStores4.add(storesAtDepth5.get(i));
		}

		BigInteger reconstructedSecret4 = reconstructor.reconstructSecretFromStores(reconStores4, n, k);
		System.out.println(reconstructedSecret4);
		System.out.println("Reconstruction successful : " + s.equals(reconstructedSecret4));
		
	}
}
